package util.io.file ;

/**
 * ADAMS Engine Core. 
 * Copyright ⓒ 2010 LGHitachi Co., Ltd. All Rights Reserved.
 * 
 * @Program Name : JDirectBufFileReader.java
 * @Description : File 및 디렉토리 Read 관리 
 * 
 * @author  : ADAMS Team
 * @version : 1.0
 * @history : 2010. 7. 30. - 최초작성
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class JDirectBufFileReader
    implements JFileReader {
    /**
     * Open 할 File Pointer를 위한 Object Reference Variable
     */
    private FileInputStream fis = null ;

    /**
     * Open 할 File 의 Name을 위한 Attribute
     */
    private String fName = null ;

    /**
     * DEFALUT BUFFER SIZE
     */
    private byte[] _BUF = null ;

    /**
     * DEFALUT CURRENT BUFFER POSITION
     */
    private int _BUF_IDX = 0 ;

    /**
     * END OF  FILE
     */
    private boolean _IS_EOF = false ;

    /**
     * DEFALUT BUFFER SIZE 
     */
    private int _BUF_SIZE = 4096 ;
    private int _LINE_SIZE = 4096 ;

    /**
     * Open 할 File 의 Default 쓰기모드로 OPEN
     */
    private final String fMode = "w" ;

    private String _LOG = "Agent";

    /**
     * CR = 개행문자,  LF = 커서를 라인의 처음으로, SP = 스페이스, EOF = END OF FILE
     */
    private final int CR = 13, LF = 10, SP = 0x20, EOF = -1, _SPACE = 32,
        _ERR = -22 ;

    /**
     * 생성자
     */
    public JDirectBufFileReader () throws FileNotFoundException {
        throw ( new FileNotFoundException (
            "Uage new JLargeBufFileReader(FileName);...." ) ) ;
    }

    /**
     * 생성자
     * 1.File Name Set
     * 2.라인사이즈 Set
     * 3.버퍼사이즈 Set
     * 4.Buffer 생성
     * 5.FileInputStream 생성
     * @param fname  open할 file의 name(절대경로)
     * @param buf_size 버퍼 사이즈
     * @param linesize 라인 사이즈
     */
    public JDirectBufFileReader ( String fname, int buf_size, int linesize ) throws
        IOException {
        if ( fname == null ) {
            throw ( new FileNotFoundException ( "FILE NAME IS NULL.." ) ) ;
        }

        if ( buf_size < 2048 ) {
            throw ( new IOException ( "Request BUF SIZE > 2038 " ) ) ;
        }

        this.fName = fname ;
        _LINE_SIZE = linesize;
        this._BUF_SIZE = buf_size ;
        _BUF = new byte[ buf_size ] ;
        fis = new FileInputStream ( this.fName ) ;
    }

    /**
     * 생성자
     * 1.File Name Set
     * 2.라인사이즈 Set
     * 3.Buffer 생성(Default : _BUF_SIZE)
     * 4.FileInputStream 생성
     * Input File Stream 생성
     * @param fname  open할 file의 name (절대경로)
     * @param linesize 라인사이즈
     */
    public JDirectBufFileReader ( String fname, int linesize ) throws FileNotFoundException {
        if ( fname == null ) {
            throw ( new FileNotFoundException ( "FILE NAME IS NULL.." ) ) ;
        }
        this.fName = fname ;
        _LINE_SIZE = linesize;
        _BUF = new byte[ _BUF_SIZE ] ;
        fis = new FileInputStream ( this.fName ) ;
    }
   
    /**
     * 생성자
     * 1. File Name Set
     * 2. 버퍼사이즈 Set
     * 3. Log Set( agent/server
     * 4. Buffer 생성
     * 5. FileInputStream 생성
     * @param fname  open할 file의 name(절대경로)
     * @param isFull file의 전체를 한번에 read할 것인지 여부(true:전체,false:buffer의 size)
     * @param linesize 라인 사이즈
     */
    public JDirectBufFileReader ( String fname, boolean isFull, int linesize ) throws
        FileNotFoundException {
        if ( fname == null ) {
            throw ( new FileNotFoundException ( "FILE NAME IS NULL.." ) ) ;
        }
        this.fName = fname ;
        _LINE_SIZE = linesize;
        if(linesize == 41 ) _LOG = "agent";
        else _LOG = "server";

        if ( isFull ) {
            _BUF_SIZE = ( int ) ( new File ( fname ) ).length () ;
            _BUF = new byte[ _BUF_SIZE ] ;
            fis = new FileInputStream ( this.fName ) ;
        } else {
            _BUF = new byte[ _BUF_SIZE ] ;
            fis = new FileInputStream ( this.fName ) ;
        }
    }

    /**
     * 파일에 대한  라인별 Read 한다.
     * @return _bd  File에서 Read한 1 Line 의  byte[]
     */
    public byte[] readLine () throws IOException {
        int b = 0 ;
        int _data = 0 ;
        int _bdsize = _LINE_SIZE ;

        byte[] _bd = new byte[ _bdsize + 5 ] ;

        if ( fis == null ) {
            throw ( new IOException ( "File Not Open...!!!" ) ) ;
        }
        
        int idx = 0 ;
        do {
            if ( _BUF_IDX == _BUF_SIZE || _BUF_IDX == 0 ) {
                if ( !_IS_EOF ) {
                    b = this.fis.read ( _BUF ) ;
                    _BUF_IDX = 0 ;
                }
            }
            
            while ( _BUF_IDX >= 0 ) {
                if ( _BUF_IDX < _BUF_SIZE ) {
                    if ( b == EOF ) {
                        this._IS_EOF = true ;
                        return null ;
                    }
                    //_data = _BUF[ _BUF_IDX++ ] ;
                    _data = (_BUF[_BUF_IDX++] & 0xFF);
                    if ( _data == _ERR ) {
                        _data = _SPACE ;
                    }

                    if ( _data == LF || _data == EOF ) {
                        break ;
                    }
                    
                    if ( idx <= _bdsize + 2   ) {
                        _bd[ idx++ ] = ( byte ) _data ;                        
                    }
                } else {
                    break ;
                }
            }
/*
            if ( idx != _bdsize ) {
                RandomAccessFile raf = new RandomAccessFile ( BaseRepository.getHomeDir() + "/logs/factor_read_error_" +
                    DateTime.getCurrentDate () + ".log", "rw" ) ;
                raf.seek ( raf.length () ) ;
                raf.write ( _bd ) ;
                raf.write ( 0x0d ) ;
                raf.write ( 0x0a ) ;
                raf.close () ;
                continue ;
            }
*/
            if ( _data == LF || this._IS_EOF  ) {
                break ;
            }
        } while ( true ) ;

        if ( b == EOF && _BUF_IDX >= _BUF_SIZE ) {
            finalize () ;
        }
        
        return _bd ;
    }
    
    
    /**
     * 파일에 대한 라인 개수를 리턴한다.
     * @return intLines 파일 라인 개수
     */
    public int getLine() throws IOException {

        int intLines = 0;

        int b = 0;
        int _data = 0;

        if (fis == null) {
            throw (new IOException("File Not Open...!!!"));
        }

        do {
            if (_BUF_IDX == _BUF_SIZE || _BUF_IDX == 0) {
                if (!_IS_EOF) {
                    b = this.fis.read(_BUF);
                    _BUF_IDX = 0;
                }
            }

            while (_BUF_IDX >= 0) {
                if (_BUF_IDX < _BUF_SIZE) {
                    if (b == EOF) {
                        break;
                    }

                    _data = (_BUF[_BUF_IDX++] & 0xFF);

                    if (_data == LF) {
                        intLines++;
                    }
                } else {
                    break;
                }
            }

            if (_data == LF || this._IS_EOF || _BUF_IDX == _BUF_SIZE) {
                break;
            }
        } while (true);

        if (b == EOF && _BUF_IDX >= _BUF_SIZE) {
            finalize();
        }

        return intLines;
    }
    

     /**
     * 파일에 대한  라인별 Read 한다.
     * @return _bd  File에서 Read한 1 Line 의  byte[]
     */
    public byte[] readLine2 () throws IOException {
        int b = 0 ;
        int _data = 0 ;
        int _bdsize = _LINE_SIZE ;
        int[] _chk={0,0,0,0,0};
        
        //#|@@\n
        int chk0 = 0x23;
        int chk1 = 0x7C;
        int chk2 = 0x40;
        int chk3 = 0x40;
        int chk4 = 0x0A;
              
        byte[] _bd = new byte[ _bdsize + 5 ] ;

        if ( fis == null ) {
            throw ( new IOException ( "File Not Open...!!!" ) ) ;
        }

        do {
            if ( _BUF_IDX == _BUF_SIZE || _BUF_IDX == 0 ) {
                if ( !_IS_EOF ) {
                    b = this.fis.read ( _BUF ) ;
                    _BUF_IDX = 0 ;
                }
            }
            int idx = 0 ;
            while ( _BUF_IDX >= 0 ) {
                if ( _BUF_IDX < _BUF_SIZE ) {
                    if ( b == EOF ) {
                        this._IS_EOF = true ;
                        return null ;
                    }
                    //_data = _BUF[ _BUF_IDX++ ] ;
                    _data = (_BUF[_BUF_IDX++] & 0xFF);
                    if ( _data == _ERR ) {
                        _data = _SPACE ;
                    }
                    _chk[0] = _chk[1];
                    _chk[1] = _chk[2];
                    _chk[2] = _chk[3];
                    _chk[3] = _chk[4];
                    _chk[4] =_data;
                    
                    //if ( _data == CR || _data == LF || _data == EOF ) {
                    if ( _data == EOF ||
                        ( _chk[0] == chk0 && _chk[1] == chk1 && _chk[2] == chk2 && _chk[3] == chk3 && _chk[4] == chk4  )
                        ) {
                        break ;
                    }else if ( _data == CR || _data == LF ) {
                        continue;
                    }
                    
                    if ( idx <= _bdsize + 2 ) {
                        _bd[ idx++ ] = ( byte ) _data ;                        
                    }
                   
                } else {
                    break ;
                }
            }
//System.out.println("0="+_chk[0]+",1="+_chk[1]+",2="+_chk[2]+",3="+_chk[3]+",4="+_chk[4]);
            if ( ( _chk[0] == chk0 && _chk[1] == chk1 && _chk[2] == chk2 && _chk[3] == chk3 && _chk[4] == chk4  )
               || this._IS_EOF || _BUF_IDX == _BUF_SIZE ) {
                                
                break ;
            }
        } while ( true ) ;

        if ( b == EOF && _BUF_IDX >= _BUF_SIZE ) {
            finalize () ;
        }        
        return _bd ;
    }

    /**
     * 파일에서 1 byte씩 Read 한다.
     * @return bret  File read 입력 스트림 데이타의 다음 바이트 Read 
     */
    public int read () throws IOException {
        int bret = SP ;
        bret = this.fis.read () ;
        if ( bret == EOF ) {
            finalize () ;
        }
        return bret ;
    }

    /**
     * Open 한 File에서 File Pointer 를 이동한다.
     * @param pos          이동할 File Pointer 의 위치
     */
    public void seek ( long pos ) throws IOException {
        fis.skip ( pos ) ;
    }

    /**
     * Open 한 File에서 특정위치에 데이타를 Read 한다.
     * @param b             File에서 Read한 byte data를 저장한 byte배열
     * @param off           File에서 Read할 시작 위치
     * @param len           File에서 Read할 byte길이
     * @return iret         File read 입력 스트림 데이타의 다음 바이트 Read
     */
    public int read ( byte b[], int off, int len ) throws IOException {
        int iret = 0 ;
        iret = fis.read ( b, off, len ) ;
        if ( iret == EOF ) {
            finalize () ;
        }
        return iret ;
    }

    /**
     * Open 한 File에서 특정위치(Range)에 데이타를 Read 한다.
     * @param b             File에서 Read 한  byte data를 저장한 byte배열
     * @param off           File에서 Read 할 시작 위치
     * @param len           File에서 Read 할  byte길이
     * @return iret         File에서 Read 한  byte수
     */
    public int read ( byte b[] ) throws IOException {
        int iret = 0 ;
        iret = fis.read ( b ) ;
        if ( iret == EOF ) {
            finalize () ;
        }
        return iret ;
    }

    /**
     * Open 한 File과 Buffer Stream을 Close해서 시스템에 자원을 반납한다.
     */
    public void finalize () {
        if ( fis != null ) {
            try {
                fis.close () ;
            } catch ( IOException e ) {
                e.printStackTrace () ;
            }
        }
    }

    /**
     * Open 한 File에 EOF인지 여부 체크한다.
     * @return     this._IS_EOF true or false 
     */
    public boolean isEOF () {
        return ( this._IS_EOF ) ;
    }
}
